package com.zhentao.dataintegrity.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhentao.dataintegrity.domain.dto.ExecutionRecordDTO;
import com.zhentao.dataintegrity.domain.dto.IntegrityTaskDTO;
import com.zhentao.dataintegrity.domain.dto.SubtaskDetailDTO;
import com.zhentao.dataintegrity.domain.entity.DataTypeConfig;
import com.zhentao.dataintegrity.domain.entity.ExecutionRecord;
import com.zhentao.dataintegrity.domain.entity.IntegrityTask;
import com.zhentao.dataintegrity.domain.entity.SubtaskDetail;
import com.zhentao.dataintegrity.mapper.DataTypeConfigMapper;
import com.zhentao.dataintegrity.mapper.ExecutionRecordMapper;
import com.zhentao.dataintegrity.mapper.IntegrityTaskMapper;
import com.zhentao.dataintegrity.mapper.SubtaskDetailMapper;
import com.zhentao.dataintegrity.service.IntegrityExecutionService;
import com.zhentao.dataintegrity.util.ExcelExportUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
public class IntegrityExecutionServiceImpl implements IntegrityExecutionService {

    private static final Logger log = LoggerFactory.getLogger(IntegrityExecutionServiceImpl.class);

    @Autowired
    private ExecutionRecordMapper executionRecordMapper;

    @Autowired
    private SubtaskDetailMapper subtaskDetailMapper;

    @Autowired
    private DataTypeConfigMapper dataTypeConfigMapper;

    @Autowired
    private IntegrityTaskMapper integrityTaskMapper;

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    @Transactional
    public String executeTask(IntegrityTaskDTO task, String triggerType, LocalDate startDate, LocalDate endDate) {
        String executionId = generateExecutionId();
        log.info("开始执行完整性检查任务: taskId={}, executionId={}, triggerType={}",
                task.getId(), executionId, triggerType);

        try {
            // 创建执行记录
            ExecutionRecord record = new ExecutionRecord();
            record.setTaskId(task.getId());
            record.setExecutionId(executionId);
            record.setTriggerType(triggerType);
            record.setExecutionStatus("RUNNING");
            record.setStartTime(LocalDateTime.now());
            record.setDataRangeStart(startDate);
            record.setDataRangeEnd(endDate);
            record.setTotalDiffRmb(BigDecimal.ZERO);
            record.setTotalDiffUsd(BigDecimal.ZERO);

            executionRecordMapper.insert(record);

            // 异步执行检查任务
            CompletableFuture.runAsync(() -> executeTaskAsync(task, executionId, startDate, endDate));

            return executionId;
        } catch (Exception e) {
            log.error("启动完整性检查任务失败: taskId={}", task.getId(), e);
            throw new RuntimeException("启动任务失败: " + e.getMessage());
        }
    }

    @Override
    public IPage<ExecutionRecordDTO> getExecutionPage(int pageNo, int pageSize, Long taskId, String executionStatus, String triggerType) {
        Page<ExecutionRecord> page = new Page<>(pageNo, pageSize);
        IPage<ExecutionRecord> recordPage = executionRecordMapper.selectPageWithTask(page, taskId, executionStatus, triggerType);

        return recordPage.convert(this::convertToDTO);
    }

    @Override
    public ExecutionRecordDTO getExecutionDetail(String executionId) {
        ExecutionRecord record = executionRecordMapper.selectByExecutionId(executionId);
        if (record == null) {
            return null;
        }

        ExecutionRecordDTO dto = convertToDTO(record);

        // 加载子任务详情
        List<SubtaskDetail> subtasks = subtaskDetailMapper.selectByExecutionId(executionId);
        dto.setSubtasks(subtasks.stream().map(this::convertSubtaskToDTO).collect(Collectors.toList()));

        return dto;
    }

    @Override
    public IPage<SubtaskDetailDTO> getSubtaskPage(int pageNo, int pageSize, String executionId, String dataTypeCode, String executionStatus) {
        Page<SubtaskDetail> page = new Page<>(pageNo, pageSize);
        IPage<SubtaskDetail> subtaskPage = subtaskDetailMapper.selectPageByExecutionId(page, executionId, dataTypeCode, executionStatus);

        return subtaskPage.convert(this::convertSubtaskToDTO);
    }

    @Override
    public List<ExecutionRecordDTO> getExecutionsByTaskId(Long taskId) {
        List<ExecutionRecord> records = executionRecordMapper.selectByTaskId(taskId);
        return records.stream().map(this::convertToDTO).collect(Collectors.toList());
    }

    @Override
    @Transactional
    public boolean cancelExecution(String executionId) {
        return executionRecordMapper.cancelExecution(executionId, LocalDateTime.now()) > 0;
    }

    @Override
    public List<ExecutionRecordDTO> getRunningExecutions() {
        List<ExecutionRecord> records = executionRecordMapper.selectRunningExecutions();
        return records.stream().map(this::convertToDTO).collect(Collectors.toList());
    }

    @Override
    public List<ExecutionRecordDTO> getRecentExecutions(int limit) {
        List<ExecutionRecord> records = executionRecordMapper.selectRecentExecutions(limit);
        return records.stream().map(this::convertToDTO).collect(Collectors.toList());
    }

    @Override
    @Transactional
    public boolean cleanHistory(int keepDays) {
        try {
            LocalDateTime beforeDate = LocalDateTime.now().minusDays(keepDays);

            // 清理子任务详情
            subtaskDetailMapper.cleanDetailsBefore(beforeDate);

            // 清理执行记录
            int count = executionRecordMapper.cleanHistoryBefore(beforeDate);

            log.info("清理了 {} 条历史记录", count);
            return true;
        } catch (Exception e) {
            log.error("清理历史记录失败", e);
            return false;
        }
    }

    @Override
    public byte[] exportTaskList(String taskTitle, String taskStatus, String triggerType) {
        try {
            // 查询任务数据
            List<IntegrityTask> tasks = integrityTaskMapper.selectList(null);

            // 转换为导出数据
            List<ExcelExportUtil.TaskExportData> exportData = tasks.stream()
                .map(this::convertToTaskExportData)
                .collect(Collectors.toList());

            // 使用工具类导出
            return ExcelExportUtil.exportTaskList(exportData);
        } catch (Exception e) {
            throw new RuntimeException("导出任务列表失败", e);
        }
    }

    @Override
    public byte[] exportSubtaskDetails(String executionId, String dataTypeCode, String executionStatus) {
        try {
            // 查询子任务数据
            List<SubtaskDetail> subtasks = subtaskDetailMapper.selectByExecutionId(executionId);

            // 转换为导出数据
            List<ExcelExportUtil.SubtaskExportData> exportData = subtasks.stream()
                .map(this::convertToSubtaskExportData)
                .collect(Collectors.toList());

            // 使用工具类导出
            return ExcelExportUtil.exportSubtaskDetails(exportData);
        } catch (Exception e) {
            throw new RuntimeException("导出子任务详情失败", e);
        }
    }

    /**
     * 异步执行任务
     */
    private void executeTaskAsync(IntegrityTaskDTO task, String executionId, LocalDate startDate, LocalDate endDate) {
        LocalDateTime startTime = LocalDateTime.now();
        BigDecimal totalDiffRmb = BigDecimal.ZERO;
        BigDecimal totalDiffUsd = BigDecimal.ZERO;

        try {
            log.info("开始异步执行完整性检查: executionId={}", executionId);

            // 获取数据类型配置
            List<DataTypeConfig> configs = dataTypeConfigMapper.selectByTypeCodes(task.getDataTypes());

            // 执行每个数据类型的检查
            for (DataTypeConfig config : configs) {
                try {
                    SubtaskDetail subtask = executeDataTypeCheck(executionId, task.getId(), config, startDate, endDate);
                    totalDiffRmb = totalDiffRmb.add(subtask.getDiffRmbAmount());
                    totalDiffUsd = totalDiffUsd.add(subtask.getDiffUsdAmount());
                } catch (Exception e) {
                    log.error("数据类型检查失败: dataType={}, error={}", config.getTypeCode(), e.getMessage(), e);

                    // 创建失败的子任务记录
                    createFailedSubtask(executionId, task.getId(), config, startDate, endDate, e.getMessage());
                }
            }

            // 更新执行记录为成功
            LocalDateTime endTime = LocalDateTime.now();
            int durationSeconds = (int) ChronoUnit.SECONDS.between(startTime, endTime);

            executionRecordMapper.updateExecutionStatus(executionId, "SUCCESS", endTime,
                durationSeconds, totalDiffRmb, totalDiffUsd, null);

            // 更新任务的最后运行信息
            integrityTaskMapper.updateRunStatus(task.getId(), startTime, "SUCCESS", null,
                totalDiffRmb, totalDiffUsd, null);

            log.info("完整性检查执行完成: executionId={}, duration={}s", executionId, durationSeconds);

        } catch (Exception e) {
            log.error("完整性检查执行失败: executionId={}", executionId, e);

            // 更新执行记录为失败
            LocalDateTime endTime = LocalDateTime.now();
            int durationSeconds = (int) ChronoUnit.SECONDS.between(startTime, endTime);

            executionRecordMapper.updateExecutionStatus(executionId, "FAILED", endTime,
                durationSeconds, totalDiffRmb, totalDiffUsd, e.getMessage());

            // 更新任务的最后运行信息
            integrityTaskMapper.updateRunStatus(task.getId(), startTime, "FAILED", e.getMessage(),
                totalDiffRmb, totalDiffUsd, null);
        }
    }

    /**
     * 执行数据类型检查
     */
    private SubtaskDetail executeDataTypeCheck(String executionId, Long taskId, DataTypeConfig config,
                                             LocalDate startDate, LocalDate endDate) throws InterruptedException {
        log.info("开始检查数据类型: dataType={}, executionId={}", config.getTypeCode(), executionId);

        LocalDateTime subtaskStartTime = LocalDateTime.now();

        // 创建子任务记录
        SubtaskDetail subtask = new SubtaskDetail();
        subtask.setExecutionId(executionId);
        subtask.setTaskId(taskId);
        subtask.setDataType(config.getTypeName());
        subtask.setDataTypeCode(config.getTypeCode());
        subtask.setExecutionStatus("RUNNING");
        subtask.setStartTime(subtaskStartTime);
        subtask.setDataRangeStart(startDate);
        subtask.setDataRangeEnd(endDate);

        subtaskDetailMapper.insert(subtask);

        try {
            // 模拟数据检查过程
            Thread.sleep(2000); // 模拟检查耗时

            // 模拟检查结果
            BigDecimal originalRmb = new BigDecimal("1000000.00");
            BigDecimal processedRmb = new BigDecimal("999950.00");
            BigDecimal originalUsd = new BigDecimal("150000.00");
            BigDecimal processedUsd = new BigDecimal("149992.50");

            BigDecimal diffRmb = processedRmb.subtract(originalRmb);
            BigDecimal diffUsd = processedUsd.subtract(originalUsd);

            // 更新子任务结果
            subtaskDetailMapper.updateSubtaskResult(subtask.getId(), "SUCCESS", LocalDateTime.now(),
                1000L, 998L, originalRmb, processedRmb, originalUsd, processedUsd,
                diffRmb, diffUsd, null, null);

            // 更新内存中的对象
            subtask.setExecutionStatus("SUCCESS");
            subtask.setEndTime(LocalDateTime.now());
            subtask.setOriginalCount(1000L);
            subtask.setProcessedCount(998L);
            subtask.setOriginalRmbAmount(originalRmb);
            subtask.setProcessedRmbAmount(processedRmb);
            subtask.setOriginalUsdAmount(originalUsd);
            subtask.setProcessedUsdAmount(processedUsd);
            subtask.setDiffRmbAmount(diffRmb);
            subtask.setDiffUsdAmount(diffUsd);

            log.info("数据类型检查完成: dataType={}, diffRmb={}, diffUsd={}",
                    config.getTypeCode(), diffRmb, diffUsd);

            return subtask;

        } catch (Exception e) {
            log.error("数据类型检查失败: dataType={}", config.getTypeCode(), e);

            // 更新子任务为失败状态
            subtaskDetailMapper.updateSubtaskResult(subtask.getId(), "FAILED", LocalDateTime.now(),
                0L, 0L, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO,
                BigDecimal.ZERO, BigDecimal.ZERO, e.getMessage(), null);

            throw e;
        }
    }

    /**
     * 创建失败的子任务记录
     */
    private void createFailedSubtask(String executionId, Long taskId, DataTypeConfig config,
                                   LocalDate startDate, LocalDate endDate, String errorMessage) {
        SubtaskDetail subtask = new SubtaskDetail();
        subtask.setExecutionId(executionId);
        subtask.setTaskId(taskId);
        subtask.setDataType(config.getTypeName());
        subtask.setDataTypeCode(config.getTypeCode());
        subtask.setExecutionStatus("FAILED");
        subtask.setStartTime(LocalDateTime.now());
        subtask.setEndTime(LocalDateTime.now());
        subtask.setDataRangeStart(startDate);
        subtask.setDataRangeEnd(endDate);
        subtask.setOriginalCount(0L);
        subtask.setProcessedCount(0L);
        subtask.setOriginalRmbAmount(BigDecimal.ZERO);
        subtask.setProcessedRmbAmount(BigDecimal.ZERO);
        subtask.setOriginalUsdAmount(BigDecimal.ZERO);
        subtask.setProcessedUsdAmount(BigDecimal.ZERO);
        subtask.setDiffRmbAmount(BigDecimal.ZERO);
        subtask.setDiffUsdAmount(BigDecimal.ZERO);
        subtask.setErrorMessage(errorMessage);

        subtaskDetailMapper.insert(subtask);
    }

    /**
     * 生成执行ID
     */
    private String generateExecutionId() {
        return "DI_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"))
               + "_" + UUID.randomUUID().toString().substring(0, 8);
    }

    /**
     * 转换执行记录为DTO
     */
    private ExecutionRecordDTO convertToDTO(ExecutionRecord record) {
        ExecutionRecordDTO dto = new ExecutionRecordDTO();
        BeanUtils.copyProperties(record, dto);

        // 设置文本字段
        dto.setTriggerTypeText(getTriggerTypeText(record.getTriggerType()));
        dto.setExecutionStatusText(getExecutionStatusText(record.getExecutionStatus()));

        if (record.getDurationSeconds() != null) {
            dto.setDurationText(formatDuration(record.getDurationSeconds()));
        }

        dto.setDataRangeText(record.getDataRangeStart() + " 至 " + record.getDataRangeEnd());

        return dto;
    }

    /**
     * 转换子任务为DTO
     */
    private SubtaskDetailDTO convertSubtaskToDTO(SubtaskDetail subtask) {
        SubtaskDetailDTO dto = new SubtaskDetailDTO();
        BeanUtils.copyProperties(subtask, dto);

        // 设置文本字段
        dto.setExecutionStatusText(getExecutionStatusText(subtask.getExecutionStatus()));
        dto.setDataRangeText(subtask.getDataRangeStart() + " 至 " + subtask.getDataRangeEnd());

        if (subtask.getStartTime() != null && subtask.getEndTime() != null) {
            long seconds = ChronoUnit.SECONDS.between(subtask.getStartTime(), subtask.getEndTime());
            dto.setDurationText(formatDuration((int) seconds));
        }

        return dto;
    }

    /**
     * 获取触发方式文本
     */
    private String getTriggerTypeText(String type) {
        Map<String, String> typeMap = new HashMap<>();
        typeMap.put("SCHEDULED", "定时");
        typeMap.put("MANUAL", "人工");
        return typeMap.getOrDefault(type, type);
    }

    /**
     * 获取执行状态文本
     */
    private String getExecutionStatusText(String status) {
        Map<String, String> statusMap = new HashMap<>();
        statusMap.put("RUNNING", "运行中");
        statusMap.put("SUCCESS", "执行完毕");
        statusMap.put("FAILED", "执行失败");
        statusMap.put("CANCELLED", "已取消");
        return statusMap.getOrDefault(status, status);
    }

    /**
     * 格式化持续时间
     */
    private String formatDuration(int seconds) {
        if (seconds < 60) {
            return seconds + "秒";
        } else if (seconds < 3600) {
            return (seconds / 60) + "分" + (seconds % 60) + "秒";
        } else {
            int hours = seconds / 3600;
            int minutes = (seconds % 3600) / 60;
            int secs = seconds % 60;
            return hours + "小时" + minutes + "分" + secs + "秒";
        }
    }

    /**
     * 转换任务为导出数据
     */
    private ExcelExportUtil.TaskExportData convertToTaskExportData(IntegrityTask task) {
        ExcelExportUtil.TaskExportData exportData = new ExcelExportUtil.TaskExportData();
        exportData.setTaskTitle(task.getTaskTitle());
        exportData.setLastRunTime(task.getLastRunTime());
        exportData.setNextRunTime(task.getNextRunTime());
        exportData.setTaskStatus(getTaskStatusText(task.getTaskStatus()));
        exportData.setLastDiffRmb(task.getLastDiffRmb());
        exportData.setLastDiffUsd(task.getLastDiffUsd());
        return exportData;
    }

    /**
     * 转换子任务为导出数据
     */
    private ExcelExportUtil.SubtaskExportData convertToSubtaskExportData(SubtaskDetail subtask) {
        ExcelExportUtil.SubtaskExportData exportData = new ExcelExportUtil.SubtaskExportData();
        exportData.setDataType(subtask.getDataType());
        exportData.setExecutionStatus(getExecutionStatusText(subtask.getExecutionStatus()));
        exportData.setStartTime(subtask.getStartTime());
        exportData.setDataRangeStart(subtask.getDataRangeStart().toString());
        exportData.setDataRangeEnd(subtask.getDataRangeEnd().toString());
        exportData.setDiffRmbAmount(subtask.getDiffRmbAmount());
        exportData.setDiffUsdAmount(subtask.getDiffUsdAmount());
        return exportData;
    }

    /**
     * 获取任务状态文本
     */
    private String getTaskStatusText(String status) {
        Map<String, String> statusMap = new HashMap<>();
        statusMap.put("ENABLED", "启用");
        statusMap.put("DISABLED", "禁用");
        return statusMap.getOrDefault(status, status);
    }
}
